package entity;

import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.persistence.Basic;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;

@Entity
@Table(name = "user")
@NamedQueries({
    @NamedQuery(name = "User.findAll", query = "SELECT u FROM User u"),
    @NamedQuery(name = "User.findByUsername", query = "SELECT u FROM User u WHERE u.username = :username"),
    @NamedQuery(name = "User.findByName", query = "SELECT u FROM User u WHERE u.name = :name"),
    @NamedQuery(name = "User.findBySurname", query = "SELECT u FROM User u WHERE u.surname = :surname"),
    @NamedQuery(name = "User.findByPassword", query = "SELECT u FROM User u WHERE u.password = :password"),
    @NamedQuery(name = "User.findByUserType", query = "SELECT u FROM User u WHERE u.userType = :userType"),
    @NamedQuery(name = "User.findByAttribute", query = "SELECT u.username, u.name, u.surname FROM User u "
            + "WHERE u.username like :keyword OR u.name like :keyword OR u.surname like :keyword")})
public class User implements Serializable {
    
    private static final long serialVersionUID = 1L;
    
    @Id
    @NotNull(message = "Cannot be empty.")
    @Size(min = 3, max = 30)
    @Pattern (regexp = "[a-zA-Z0-9]*")
    @Column(name = "username")
    private String username;
    
    @Basic(optional = false)
    @NotNull(message = "Cannot be empty.")
    @Size(min = 2, max = 30)
    @Pattern (regexp = "[a-zA-Z]*")
    @Column(name = "name")
    private String name;
    
    @Basic(optional = false)
    @NotNull(message = "Cannot be empty.")
    @Size(min = 2, max = 30)
    @Pattern (regexp = "[a-zA-Z]*")
    @Column(name = "surname")
    private String surname;
    
    @Basic(optional = false)
    @NotNull(message = "Cannot be empty.")
    @Size(min = 1, max = 64)
    @Column(name = "password")
    private String password;
    
    @Basic(optional = false)
    @NotNull
    @Size(min = 1, max = 30)
    @Column(name = "userType")
    private String userType;
    
    @ManyToMany(mappedBy = "participants")
    private List<Event> joiningEvents = new ArrayList<>();
    
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "owner")
    private List<Calendar> calendars = new ArrayList<>();
    
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "participant")
    private List<Notification> notifications = new ArrayList<>();
    
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "sender")
    private List<Invitation> pendingSentInvitations = new ArrayList<>();
    
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "receiver")
    private List<Invitation> receivedInvitations = new ArrayList<>();
    
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "creator")
    private List<Event> createdEvents = new ArrayList<>();

    public User() {
    }

    public User(String username) {
        this.username = username;
    }

    public User(String username, String name, String surname, String password, String userType) {
        this.username = username;
        this.name = name;
        this.surname = surname;
        this.password = password;
        this.userType = userType;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSurname() {
        return surname;
    }

    public void setSurname(String surname) {
        this.surname = surname;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        try {
            MessageDigest digest = MessageDigest.getInstance("SHA-256");
            byte[] hash = digest.digest(password.getBytes("UTF-8"));
            BigInteger bigInt = new BigInteger(1, hash);
            this.password = bigInt.toString(16);
        }
        catch (NoSuchAlgorithmException | UnsupportedEncodingException ex) {
            Logger.getLogger(User.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public String getUserType() {
        return userType;
    }

    public void setUserType(String userType) {
        this.userType = userType;
    }

    public List<Event> getJoiningEvents() {
        return joiningEvents;
    }

    public void setJoiningEvents(List<Event> joiningEvents) {
        this.joiningEvents = joiningEvents;
    }
    
    public void addJoiningEvent(Event joiningEvent) {
        joiningEvents.add(joiningEvent);
    }
    
    public void removeJoiningEvent(Event joiningEvent) {
        joiningEvents.remove(joiningEvent);
    }

    public List<Calendar> getCalendars() {
        return calendars;
    }

    public void setCalendars(List<Calendar> calendars) {
        this.calendars = calendars;
    }
    
    public void addCalendar(Calendar calendar) {
        calendars.add(calendar);
    }
    
    public void removeCalendar(Calendar calendar) {
        calendars.remove(calendar);
    }

    public List<Notification> getNotifications() {
        return notifications;
    }

    public void setNotifications(List<Notification> notifications) {
        this.notifications = notifications;
    }
    
    public void addNotification(Notification notification) {
        notifications.add(notification);
    }
    
    public void removeNotification(Notification notification) {
        notifications.remove(notification);
    }

    public List<Invitation> getPendingSentInvitations() {
        return pendingSentInvitations;
    }

    public void setPendingSentInvitations(List<Invitation> pendingSentInvitations) {
        this.pendingSentInvitations = pendingSentInvitations;
    }
    
    public void addPendingSentInvitation(Invitation pendingInvitation) {
        pendingSentInvitations.add(pendingInvitation);
    }
    
    public void removePendingSentInvitation(Invitation pendingInvitation) {
        pendingSentInvitations.remove(pendingInvitation);
    }

    public List<Invitation> getReceivedInvitations() {
        return receivedInvitations;
    }

    public void setReceivedInvitations(List<Invitation> receivedInvitations) {
        this.receivedInvitations = receivedInvitations;
    }
    
    public void addReceivedInvitation(Invitation receivedInvitation) {
        receivedInvitations.add(receivedInvitation);
    }
    
    public void removeReceivedInvitation(Invitation receivedInvitation) {
        receivedInvitations.remove(receivedInvitation);
    }

    public List<Event> getCreatedEvents() {
        return createdEvents;
    }
    
    public void addCreatedEvent(Event createdEvent) {
        createdEvents.add(createdEvent);
    }
    
    public void removeCreatedEvent(Event createdEvent) {
        createdEvents.remove(createdEvent);
    }

    public void setCreatedEvents(List<Event> createdEvents) {
        this.createdEvents = createdEvents;
    }

    @Override
    public int hashCode() {
        int hash = 0;
        hash += (username != null ? username.hashCode() : 0);
        return hash;
    }

    @Override
    public boolean equals(Object object) {
        if (!(object instanceof User)) {
            return false;
        }
        User other = (User) object;
        return Objects.equals(this.username, other.username);
    }

    @Override
    public String toString() {
        return "entity.User[ username=" + username + " ]";
    }
}
